package com.xingfly.school.student.domain;

import com.xingfly.school.course.acl.pl.response.StudentResponse;
import com.xingfly.school.student.acl.pl.response.CourseResponse;
import com.xingfly.school.student.acl.ports.clients.CourseClient;
import com.xingfly.school.student.acl.ports.repositories.StudentCourseRegistrationRepository;
import com.xingfly.school.student.acl.ports.repositories.StudentRepository;
import com.xingfly.school.student.ohs.local.pl.response.StudentCourseInfoResponse;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Created by SuperS on 2020/3/27.
 *
 * @author SuperS
 */
@Service
public class StudentDomainService {

    @Resource
    private StudentRepository studentRepository;

    @Resource
    private StudentCourseRegistrationRepository studentCourseRegistrationRepository;

    @Resource
    private CourseClient courseClient;

    public List<StudentCourseInfoResponse> findCoursesByStudentId(Long studentId) {
        Student student = studentRepository.findById(studentId)
                .orElseThrow(() -> new RuntimeException("学生不存在"));
        List<StudentCourseRegistration> courseRegistrations = studentCourseRegistrationRepository.findAll(
                (Specification<StudentCourseRegistration>) (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.equal(root.get("studentId"), student.getId())
        );
        List<CourseResponse> courses = courseClient.findAllById(courseRegistrations.stream()
                .map(StudentCourseRegistration::getCourseId)
                .collect(Collectors.toList())
        );
        Map<Long, LocalDateTime> joinTimeMap = courseRegistrations.stream()
                .collect(Collectors.toMap(StudentCourseRegistration::getCourseId, StudentCourseRegistration::getRegistrationTime));
        return courses.stream()
                .map(course -> {
                    LocalDateTime joinTime = joinTimeMap.get(course.getId());
                    return StudentCourseInfoResponse.builder()
                            .courseId(course.getId())
                            .courseName(course.getName())
                            .joinDateTime(joinTime)
                            .build();
                }).collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    public Boolean registration(Long studentId, Long courseId) {
        Student student = studentRepository.findById(studentId).orElse(null);
        CourseResponse courseResponse = courseClient.findById(courseId);
        if (student != null && courseResponse.getId() != null) {
            studentCourseRegistrationRepository.save(StudentCourseRegistration.of(studentId, courseId));
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    public List<StudentResponse> findStudentsByRegistrationIds(Set<Long> studentRegistrationIds) {
        List<StudentCourseRegistration> registrations = studentCourseRegistrationRepository.findAllById(studentRegistrationIds);
        List<Student> students = studentRepository.findAllById(registrations.stream()
                .map(StudentCourseRegistration::getStudentId).collect(Collectors.toList()));
        return students.stream().map(student -> {
            StudentCourseRegistration registration = registrations.stream()
                    .filter(r -> student.getId().equals(r.getStudentId())).findFirst().orElse(new StudentCourseRegistration());
            return StudentResponse.builder()
                    .studentId(student.getId())
                    .name(student.getName())
                    .joinTime(registration.getRegistrationTime())
                    .build();
        }).collect(Collectors.toList());

    }

    public Set<Long> findRegistrationIdsByCourseId(Long courseId) {
        List<StudentCourseRegistration> registrations = studentCourseRegistrationRepository.findAll(
                (Specification<StudentCourseRegistration>) (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.equal(root.get("courseId"), courseId)
        );
        return registrations.stream().map(StudentCourseRegistration::getId).collect(Collectors.toSet());
    }
}
